/* 
 * MacosxPolisher.java
 * Copyright (c) 2008 WayfarerX.net and others (see below).
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * Contributors:
 *   WayfarerX (2008) - Initial documentation & implementation.
 * 
 */
package net.wayfarerx.tools.export.internal;

import java.io.File;
import java.util.Arrays;
import java.util.LinkedHashSet;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.eclipse.core.runtime.IProgressMonitor;
import org.osgi.framework.Version;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * Polisher that packages the runtime in a Mac OS X application bundle.
 * 
 * @author WayfarerX
 */
final class MacosxPolisher extends ProductPolisher {
	private static final String JAR_PREFIX = "org.eclipse.equinox.launcher_"; //$NON-NLS-1$
	private static final String LIB_PREFIX =
	    "org.eclipse.equinox.launcher.carbon.macosx_"; //$NON-NLS-1$

	/*
	 * (non-Javadoc)
	 * 
	 * @see net.wayfarerx.tools.export.internal.ProductPolisher#polish(
	 *      org.eclipse.core.runtime.IProgressMonitor, java.io.File)
	 */
	@Override
	void polish(IProgressMonitor monitor, File rootDirectory) throws Exception {
		// Index the exported contents.
		LinkedHashSet<String> children = new LinkedHashSet<String>();
		children.addAll(Arrays.asList(rootDirectory.list()));
		children.remove("."); //$NON-NLS-1$
		children.remove(".."); //$NON-NLS-1$
		File tmp = new File(rootDirectory, ".tmp").getCanonicalFile(); //$NON-NLS-1$
		tmp.mkdirs();
		File appBundle = null;
		for (String child : children) {
			if (child.endsWith(".app")) //$NON-NLS-1$
				appBundle = new File(rootDirectory, child).getCanonicalFile();
			else
				new File(rootDirectory, child).getCanonicalFile().renameTo(
				    new File(tmp, child).getCanonicalFile());
		}
		children.clear();
		// Determine the base structure.
		File contents = new File(appBundle, "Contents").getCanonicalFile(); //$NON-NLS-1$
		File infoPlist = new File(contents, "Info.plist").getCanonicalFile(); //$NON-NLS-1$
		File macos = new File(contents, "MacOS").getCanonicalFile(); //$NON-NLS-1$
		File executable =
		    new File(macos, appBundle.getName().substring(0,
		        appBundle.getName().length() - 4)).getCanonicalFile();
		File resources = new File(contents, "Resources").getCanonicalFile(); //$NON-NLS-1$
		// Move the eclipse install area into the application bundle.
		File location = new File(resources, "Java").getCanonicalFile(); //$NON-NLS-1$
		tmp.renameTo(location);
		// Determine the structure of the install area.
		File plugins = new File(location, "plugins").getCanonicalFile(); //$NON-NLS-1$
		File launcherBundle = null, launcherFragment = null;
		Version jarVersion = null, libVersion = null;
		for (String child : plugins.list()) {
			if (child.startsWith(JAR_PREFIX)) {
				Version version =
				    new Version(child
				        .substring(JAR_PREFIX.length(), child.length() - 4));
				if (jarVersion == null || jarVersion.compareTo(version) < 0) {
					launcherBundle = new File(plugins, child).getCanonicalFile();
					jarVersion = version;
				}
			} else if (child.startsWith(LIB_PREFIX)) {
				Version version =
				    new Version(child.substring(LIB_PREFIX.length(), child.length()));
				if (libVersion == null || libVersion.compareTo(version) < 0) {
					launcherFragment = new File(plugins, child).getCanonicalFile();
					libVersion = version;
				}
			}
		}
		File launcherLib = null;
		for (String child : launcherFragment.list()) {
			if (child.endsWith(".so")) //$NON-NLS-1$
				launcherLib = new File(launcherFragment, child).getCanonicalFile();
		}
		// Modify the Info.plist file.
		if (infoPlist.isFile()) {
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			dbf.setValidating(false);
			dbf.setNamespaceAware(false);
			DocumentBuilder db = dbf.newDocumentBuilder();
			Document doc = db.parse(infoPlist);
			NodeList list =
			    ((Element) doc.getDocumentElement()
			        .getElementsByTagName("dict").item(0)).getChildNodes(); //$NON-NLS-1$
			boolean next = false;
			for (int i = 0; i < list.getLength(); ++i) {
				Node n = list.item(i);
				if (!(n instanceof Element))
					continue;
				Element e = (Element) n;
				if (next) {
					e.appendChild(doc.createTextNode("\t")); //$NON-NLS-1$
					Element s = doc.createElement("string"); //$NON-NLS-1$
					s.setTextContent("--launcher.library"); //$NON-NLS-1$
					e.appendChild(s);
					e.appendChild(doc.createTextNode("\n\t\t\t")); //$NON-NLS-1$
					s = doc.createElement("string"); //$NON-NLS-1$
					s.setTextContent(String.format(
					    "$APP_PACKAGE/Contents/Resources/Java/plugins/%s/%s", //$NON-NLS-1$
					    launcherFragment.getName(), launcherLib.getName()));
					e.appendChild(s);
					e.appendChild(doc.createTextNode("\n\t\t\t")); //$NON-NLS-1$
					s = doc.createElement("string"); //$NON-NLS-1$
					s.setTextContent("-startup"); //$NON-NLS-1$
					e.appendChild(s);
					e.appendChild(doc.createTextNode("\n\t\t\t")); //$NON-NLS-1$
					s = doc.createElement("string"); //$NON-NLS-1$
					s.setTextContent(String.format(
					    "$APP_PACKAGE/Contents/Resources/Java/plugins/%s", //$NON-NLS-1$
					    launcherBundle.getName()));
					e.appendChild(s);
					e.appendChild(doc.createTextNode("\n\t\t\t")); //$NON-NLS-1$
					s = doc.createElement("string"); //$NON-NLS-1$
					s.setTextContent("-install"); //$NON-NLS-1$
					e.appendChild(s);
					e.appendChild(doc.createTextNode("\n\t\t\t")); //$NON-NLS-1$
					s = doc.createElement("string"); //$NON-NLS-1$
					s.setTextContent("$APP_PACKAGE/Contents/Resources/Java"); //$NON-NLS-1$
					e.appendChild(s);
					e.appendChild(doc.createTextNode("\n\t\t\t")); //$NON-NLS-1$
					s = doc.createElement("string"); //$NON-NLS-1$
					s.setTextContent("-configuration"); //$NON-NLS-1$
					e.appendChild(s);
					e.appendChild(doc.createTextNode("\n\t\t\t")); //$NON-NLS-1$
					s = doc.createElement("string"); //$NON-NLS-1$
					s
					    .setTextContent("$APP_PACKAGE/Contents/Resources/Java/configuration"); //$NON-NLS-1$
					e.appendChild(s);
					e.appendChild(doc.createTextNode("\n\t\t\t")); //$NON-NLS-1$
					s = doc.createElement("string"); //$NON-NLS-1$
					s.setTextContent("-data"); //$NON-NLS-1$
					e.appendChild(s);
					e.appendChild(doc.createTextNode("\n\t\t\t")); //$NON-NLS-1$
					s = doc.createElement("string"); //$NON-NLS-1$
					s.setTextContent(String.format(
					    "@user.home/Library/Application Support/%s", //$NON-NLS-1$
					    executable.getName()));
					e.appendChild(s);
					e.appendChild(doc.createTextNode("\n\t\t")); //$NON-NLS-1$
					TransformerFactory.newInstance().newTransformer().transform(
					    new DOMSource(doc), new StreamResult(infoPlist));
					break;
				} else {
					if ("key".equals(e.getTagName()) //$NON-NLS-1$
					    && "Eclipse".equals(e.getTextContent())) //$NON-NLS-1$
						next = true;
				}
			}
		}
		// Done.
		monitor.worked(100);
	}
}
